Zerm 2.8.0 — meeting recording, speaker identification, dashboard scroll fix - #291
Merged
Conversation
The release workflow's new drift check caught this on its first run: BUILDING.md gained the post-release changelog instructions, but docs/building.html is generated from it and was never rebuilt, so the published page did not show them. Exactly the failure mode the check exists for.
Chart selection follows the pointer, so scrolling with the cursor over a chart rewrote the selection on every event. Each write rebuilt both charts, and the nearest-bucket search was read from inside the mark builders, making it O(n) per plotted element. Selection now snaps to a bucket and is written only when the bucket actually changes, so pointer movement inside one bar costs nothing. Everything derived from the buckets — rate points, peak, x-domain, the bucket index — is computed once in UsageChartSeries rather than on every body pass, and the nearest-bucket lookup is a binary search. Card materials are replaced with an opaque surface: eight blur passes inside one scroll view were redrawn every frame for a result that reads almost identically over the window background. The hero shadow drops from 30pt to 12pt for the same reason. The data table is lazy, and reloads triggered by usage notifications are coalesced. Refs #288
App discovery resolved symlinks and recursed into whatever they pointed at, guarded only by a depth limit. A link into a large or remote tree turned opening the Power Mode app picker into a long filesystem walk, and the same bundle could be collected several times under different paths. Replaced with a non-following enumeration that dedupes by standardised path. Selecting Apple's native model with a locale whose assets were not downloaded was a permanent dead end: the service threw an error naming the missing language, and nothing anywhere in the app could fetch it. It now requests the installation and re-checks installedLocales, surfacing the old error only if the download genuinely fails. Refs #290
A Recording tab that captures a whole conversation — in the room, or a call on Teams, Meet, Signal or anything else — as opposed to the short push-to-talk dictation Zerm does today. The microphone and the system output are captured as separate tracks. Mixing them at capture time would be cheaper, but two tracks mean the local room and the remote participants are already separated on disk, which is speaker attribution without diarisation and lets either side be re-transcribed alone. System audio uses a Core Audio process tap: no driver to install, no kernel extension, and no screen-recording permission. Transcription cuts the audio into overlapping windows and transcribes each as a file rather than holding a streaming socket open. Over a two-hour meeting that matters — sockets drop, cloud streaming meters by the minute, and a dropped connection loses the tail. A window that fails costs only itself, and the approach works with every model Zerm supports. Speaker identification uses FluidAudio's LS-EEND diarizer, already a dependency, fed the microphone track alone. Transcript lines are attributed by overlap rather than index, since windows and turns are cut on different boundaries. Summaries fold long transcripts — chunk summaries, then a summary of those — so a two-hour meeting works on the same small-context local model as a short one. Routed to local Ollama by default: a meeting transcript is the most sensitive text Zerm handles. Recordings are filesystem-backed rather than another store: a recording is its folder, so nothing can drift out of sync and moving or deleting in Finder does what the user expects. Every transcript line is journalled as it lands, so a crash costs the window in flight rather than the whole meeting. Also handles the failure modes that are silent otherwise: an unauthorised process tap returns success and delivers pure silence, so an all-zero capture is detected and surfaced; the default output device changing mid-call is followed by rebuilding the aggregate, which previously left the far side recording nothing for the rest of the meeting. Refs #289
Six alternating turns from two different system voices, fed through the live chunking path. The diarizer produces turns but labels both voices as the same speaker, so every transcript line would be attributed to "Speaker 1". Committed failing on purpose. Softening it would hide the only question that matters about speaker identification: whether it actually separates speakers. Whether this is a limitation of synthetic voices or of the configuration is not yet known — two real voices will settle it. Refs #289
The LS-EEND model runs at 8 kHz. Everything else in Zerm — the tap writer, the transcriber, the microphone — is 16 kHz, and addAudio(_:) means "these samples are already at the model rate". So 16 kHz audio was being fed to an 8 kHz model with no resampling. Nothing failed. The model still produced turns, so the feature looked wired and working, but two clearly different voices decoded as a single speaker and every transcript line would have been attributed to "Speaker 1". Declaring sourceSampleRate lets the library resample, and the two-speaker test that was committed failing now passes. The capacity diagnostic that found this is kept as a regression guard on the sample-rate contract, since the same mistake would degrade silently again. Refs #289
Meeting recording, speaker identification and summaries, plus the dashboard scroll fix and two upstream ports. A new tab and a new capture engine is a feature release, not a patch.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a Recording tab that captures whole conversations — in the room, or a call on Teams, Meet, Signal or anything else — alongside the short push-to-talk dictation Zerm already does.
Closes #289. Refs #288, #290.
Recording
Also
Dashboard scroll performance (#288) and two upstream VoiceInk ports (#290) — symlink-following app discovery, and Apple's native model being a dead end when locale assets were missing.
Seven defects fixed, all of which failed silently
didAttemptLoadreporting false after teardownqueue.syncstate handling in the diarizerNone would have been caught by the build.
Verification
74/74 tests passing, warning-free build. Capture, conversion, disk writing, session lifecycle and two-speaker separation all exercised against real audio.
Not yet validated: a real meeting between two people. Speaker separation is proven on two synthetic voices, which are easier to tell apart than two humans sharing one microphone in a room with crosstalk.